/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is graber for mozrepl.
 *
 * The Initial Developer of the Original Code is
 * Irakli Gozalishvili Irakli Gozalishvili <rfobic [at] gmail [dot] com>
 * Portions created by the Initial Developer are Copyright (C) 2006-2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Irakli Gozalishvili <rfobic [at] gmail [dot] com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

var grabInfinite = false;

function grab(infinite){
    grabInfinite = infinite || false;
    var grabOnce = true;
    var context = this._workContext;
    var target = { value : undefined };
    
    var blinkObject = function(target,number){
        var params = {
            originalBorder : target.style.MozOutline,
            changedBorder : "1px dotted red",
            target : target,
            number : number || 3,
            window : context.window
        };
        
        var flashOff = function(){
            var params = arguments[0];
            params.target.style.MozOutline = params.originalBorder;
            if (params.number > 0) {
                params.window.setTimeout(flashOn,100,params);
            }
        };

        var flashOn = function(){
            var params = arguments[0];
            params.number --;
            params.target.style.MozOutline = params.changedBorder;
            params.window.setTimeout(flashOff,100,params);
        };
        
        context.window.setTimeout(flashOn,100,params);
    };
    
    var grabber = function(event){
        if (grabInfinite || grabOnce) {
            grabOnce = false;
            event.preventDefault();
            event.stopPropagation();
            target.value = event.target;
            blinkObject(target.value);
        } else if (!grabInfinite){
            context.window.removeEventListener("click", grabber, true);
        }
    };
    context.window.addEventListener("click",grabber,true);
    return target;
}

grab.doc = "Grabs object which was clicked (If argument true is passed grubs untill grabMouseStrop is called)";

function grabStop(){
    grabInfinite = false;
}
grabStop.doc = "Stops grabbing objects";

defineInteractor('http-inspect', {
    handleInput: function(repl, input) {
        // Given an HTTP _request_, return an array containing the verb,
        // the path, and protocol version, e.g. ['GET', '/foo/bar', 'HTTP/1.1']
        //
        // Careful, the implementation is as naive as it can get!'

        function parseHTTPRequest(request) {
            return request
                .split(/[\r\n]+/)[0] // get first line
                .split(/\s+/); // split it
        }

        // Given a _startContext_ object it resolves a _path_ to a value.
        //
        // Example:
        //
        //   _resolveObjectPath({
        //       foo: {
        //          bar: 'hello',
        //          baz: 'world'
        //       }
        //   }, '/foo/bar') // #=> 'hello'

        function resolveObjectPath(startContext, path) {
            return path
                .split('/')
                .reduce(
                    function(context, pathStep) pathStep == '' ?  context :  context[pathStep],
                    startContext);
        }

        // Strip leading and trailing whitespace
        var input = input.replace(/^\s+|\s+$/g, '');

        switch(input) {
            // "Magic" commands.  Use them when you're developing the
            // interactor from telnet, to avoid stopping and
            // restarting the REPL after each change.
        case 'q':
            repl.setInteractor('javascript');
            repl._prompt();
            break;

        case 'r':
            repl.load(Cc['@mozilla.org/preferences-service;1']
                      .getService(Ci.nsIPrefBranch)
                      .getCharPref('extensions.mozrepl.initUrl'), repl);
            break;
        default:
            var [verb, path, protocol_version] = parseHTTPRequest(input);
            if(verb != 'GET')
                return;

            var target = resolveObjectPath(repl._hostContext, path);

            var content = <tbody/>;
            for(var propName in target) {
                var propType;
                try {
                    propType = typeof(target[propName]);
                } catch(e) {
                    propType = '[exception]';
                }

                content.appendChild(
                    <tr>
                        <td><a href={path + (path.slice(-1) == '/' ? '' : '/') + propName}>{propName}</a></td>
                        <td>{propType}</td>
                    </tr>
                );
            }

            var targetType = typeof(target);
            var targetRepresentation = target.toString();

            var breadcrumbs = <div id="breadcrumb"><a href="/">[root]</a></div>;

            var pathSteps = path.split('/');
            for(let i=1; i<pathSteps.length; i++) {
                breadcrumbs.appendChild('/')
                breadcrumbs.appendChild(<a href={pathSteps.slice(0,i+1).join('/')}>{pathSteps[i]}</a>);
            }
            breadcrumbs.appendChild('(' + targetType + ')');

            repl.print('HTTP/1.1 200 OK');
            repl.print("Server: you wouldn't believe. :D");
            repl.print('Content-Type: application/xhtml+xml');
            repl.print();
            repl.print(
                <html xmlns="http://www.w3.org/1999/xhtml">
                  <head>
                    <title>Meta-Browser</title>
                    <style type="text/css">
                    <![CDATA[
                        html {
                            font-family: "Trebuchet MS", Verdana, Helvetica, Arial, sans-serif;
                            background: lightblue;
                        }
                        body { margin: 1em; }
                        td { padding: 0.1em 0.5em; }
                        h2 { text-align: center; }
                        #content {
                            -moz-border-radius: 1em;
                            width: 40em; margin: auto;
                            background: white;
                            padding: 0.5em 1.5em 2em 1.5em;
                            min-height: 30em;
                        }
                        #representation {
                            white-space: -moz-pre-wrap;
                            overflow: auto;
                            background: #f2f2f2;
                            padding: 1em;
                        }
                        #properties { width: 100%; }
                        td { background: #f2f2f2; }
                    ]]>
                    </style>
                  </head>
                  <body>
                    <div id="content">
                      <h2>Context</h2>
                      {breadcrumbs}

                      <h2>Representation</h2>
                      <pre id="representation">{targetRepresentation}</pre>

                      <h2>Properties</h2>
                      <table id="properties">
                        <thead>
                          <tr><th>Name</th><th>Type</th></tr>
                        </thead>
                        {content}
                      </table>
                    </div>
                  </body>
                </html>
            );

            // Don't keep connection open
            repl.quit();
        }
    },

    onStart: function(repl) {},

    getPrompt: function(repl) {},
});

defineInteractor('inspect', {
    onStart: function(repl) {
    },

    getPrompt: function(repl) {
        return '\nLocation: ' + represent(repl._workContext) + '\ni> ';
    },

    handleInput: function(repl, input) {
        var [command, arg] = input.replace(/^\s+|\s+$/g, '').split(/\s+/, 2);

        switch(command) {
        case 'q':
            repl.setInteractor('javascript');
            break;

        case 'l':
            repl.look();
            break;

        case 's':
            repl.print((arg ? repl._workContext : repl._workContext).toString());
            break;

        case 'e':
            repl.enter(repl._workContext[arg]);
            break;

        case 'b':
            repl.back();
            break;

        case 'h':
            repl.print('l        : look around in current context');
            repl.print('e <name> : enter object pointed to by <name>');
            repl.print('s <name> : show object poined by <name> using toString()');
            repl.print('b        : go back to previous context');
            repl.print('h        : this help text');
            repl.print('q        : quit inspector, go back to javascript interactor');
            break;

        default:
            repl.print('Uknown command.');
            break;
        }
        repl._prompt();
    }
});
